package org.tigris.atlas.xml.schema.mda.generator;

import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import org.apache.velocity.VelocityContext;
import org.tigris.atlas.mda.element.java.JavaComposite;
import org.tigris.atlas.mda.element.java.JavaEntity;
import org.tigris.atlas.mda.element.java.JavaEnumeration;
import org.tigris.atlas.mda.generator.AbstractJavaGenerator;
import org.tigris.atlas.mda.generator.GenerationContext;
import org.tigris.atlas.mda.generator.GenerationException;
import org.tigris.atlas.mda.metadata.MetadataRepository;
import org.tigris.atlas.mda.metadata.element.Composite;
import org.tigris.atlas.mda.metadata.element.Entity;
import org.tigris.atlas.mda.metadata.element.Reference;
import org.tigris.atlas.mda.metadata.element.Relation;
import org.tigris.atlas.xml.schema.mda.element.SchemaElementUtils;
import org.tigris.atlas.xml.schema.mda.generator.SchemaContainer;

public class SchemaJavaEntityGenerator extends AbstractJavaGenerator {

	public void generate(GenerationContext context) throws GenerationException {
		String applicationName = context.getArtifactId();
		Iterator entities = MetadataRepository.getInstance().getAllEntities(applicationName).values().iterator();
		
		String fileName;
		String basefileName = context.getOutputFile();		
		basefileName = replaceBasePackage(basefileName, context.getBasePackageAsPath());
		while (entities.hasNext()) {
			Entity entity = (Entity) entities.next();
			JavaEntity javaEntity = new JavaEntity(entity);
			
			VelocityContext vc = new VelocityContext();
			vc.put("entity", javaEntity);
			vc.put("basePackage", context.getBasePackage());
			vc.put("prefix", context.getBasePackage());
			
			String baseNamespace = SchemaElementUtils.translateBasePackageToBaseSchemaLocation(context.getBasePackage());
			vc.put("baseNamespace", baseNamespace);
			
			//combine composite and entity imports:
			Set importSet = new HashSet();
			importSet.addAll(javaEntity.getFieldImports());
			JavaComposite javaComposite;
			Collection entityComposites = javaEntity.getComposites().values();
			Iterator i = entityComposites.iterator();
			while (i.hasNext()) {
				javaComposite = (JavaComposite)i.next();
				importSet.addAll(javaComposite.getFieldImports());
			}
			vc.put("imports", importSet);
			
			SchemaContainer schemaContainer = new SchemaContainer();
			registerAllEntities(javaEntity, schemaContainer);
			vc.put("schemaContainer", schemaContainer);
			importSet.addAll(getEnumerationImports(schemaContainer));
			
			fileName = replaceEntityName(basefileName, entity.getName());
			context.setOutputFile(fileName);
			
			generateFile(context, vc);
		}
	}

	private Collection getEnumerationImports(SchemaContainer schemaContainer) {
		Collection imports = new HashSet();
		for (Iterator i = schemaContainer.getEnumerations().iterator(); i.hasNext();) {
			JavaEnumeration enumer = (JavaEnumeration) i.next();
			imports.add(enumer.getImport());
		}
		return imports;
	}

	private void registerAllEntities(Entity entity, SchemaContainer schemaContainer) {
		if (entity != null) {
			Map metadataEntityMap = MetadataRepository.getInstance().getAllEntities();
			
			//add this entity for easier generic generation:
			schemaContainer.addEntity(entity);
			
			//check for references:
			Map referenceMap = entity.getReferences();
			if ((referenceMap != null) && (referenceMap.size() > 0)) {
				String entityName;
				Reference reference;
				Entity referencedEntity;
				Iterator i = referenceMap.values().iterator();
				while (i.hasNext()) {
					reference = (Reference)i.next();
					entityName = reference.getType();
					referencedEntity = (Entity)metadataEntityMap.get(entityName);			
					if (!schemaContainer.containsEntity(entityName)) {
						schemaContainer.addEntity(new JavaEntity(referencedEntity));
						registerAllEntities(referencedEntity, schemaContainer);
					}
				}
				
			}
			
			//check relations:
			Map relationMap = entity.getRelations();
			if ((relationMap != null) && (relationMap.size() > 0)) {
				String entityName;
				Relation relation;
				Entity relatedEntity;
				Iterator i = relationMap.values().iterator();
				while (i.hasNext()) {
					relation = (Relation)i.next();
					entityName = relation.getType();
					relatedEntity = (Entity)metadataEntityMap.get(entityName);			
					if (!schemaContainer.containsEntity(entityName)) {
						schemaContainer.addEntity(new JavaEntity(relatedEntity));
						registerAllEntities(relatedEntity, schemaContainer);
					}
				}
				
			}
			
			//check composites:
			Map compositeMap = entity.getComposites();
			if ((compositeMap != null) && (compositeMap.size() > 0)) {
				Map metadataCompositeMap = MetadataRepository.getInstance().getAllComposites();
				String compositeName;
				Composite composite;
				Composite referencedComposite;
				Iterator i = compositeMap.values().iterator();
				while (i.hasNext()) {
					composite = (Composite)i.next();
					compositeName = composite.getType();
					referencedComposite = (Composite)metadataCompositeMap.get(compositeName);			
					if (!schemaContainer.containsComposite(compositeName)) {
						schemaContainer.addComposite(new JavaComposite(referencedComposite));					
					}
				}
				
			}
		}
	}

}
